package com.biz.primus.ms.sms.service;

import com.biz.primus.base.exception.utils.AssertUtils;
import com.biz.primus.common.utils.MathUtil;
import com.biz.primus.model.member.vo.MemberVo;
import com.biz.primus.model.misc.enums.SMSType;
import com.biz.primus.model.misc.exception.SmsExceptions;
import com.biz.primus.model.misc.vo.ShortMessageVo;
import com.biz.primus.ms.sms.dao.redis.SmsRedisDao;
import com.biz.primus.ms.sms.dao.ro.SmsRo;
import com.biz.primus.ms.sms.feign.MemberFeignClient;
import com.biz.primus.ms.sms.stream.SmsSendSource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author yanweijin
 * @date 2017/9/12
 */
@Service
@Slf4j
public class SMSService {
    private final ShortMessageService shortMessageService;
    private final MemberFeignClient memberFeignClient;
    private final SmsTemplateService smsTemplateService;
    private final SmsRedisDao smsRedisDao;
    private final SmsSendSource smsSendSource;
    /**
     * 短信验证码有效时间,秒(默认10分钟)
     */
    @Value("${sms.validTimeInSecond:600}")
    private int validTimeInSecond;

    /**
     * 短信验证码重发间隔时间,秒(默认1分钟)
     */
    @Value("${sms.sendDurationInSecond:60}")
    private int sendDurationInSecond;

    @Autowired
    public SMSService(ShortMessageService shortMessageService, MemberFeignClient memberFeignClient, SmsTemplateService smsTemplateService, SmsRedisDao smsRedisDao, SmsSendSource smsSendSource) {
        this.shortMessageService = shortMessageService;
        this.memberFeignClient = memberFeignClient;
        this.smsTemplateService = smsTemplateService;
        this.smsRedisDao = smsRedisDao;
        this.smsSendSource = smsSendSource;
    }

    /**
     * 发送短信
     *
     * @param username 用户名
     * @param smsType  短信类型
     */
    public void sendSMS(String username, SMSType smsType) {
        String mobile = username;
        MemberVo memberVo = memberFeignClient.findByAccount(username);
        switch (smsType) {
            case FORGOT_PASSWORD:
                AssertUtils.notNull(memberVo, SmsExceptions.USER_NOT_EXIST);
                mobile = memberVo.getMobile();
                AssertUtils.hasLength(mobile, SmsExceptions.MOBILE_NOT_FOUND);
                break;
            case REGISTER:    //注册时只能通过手机注册
                AssertUtils.hasLength(mobile, SmsExceptions.MOBILE_ERROR);
                AssertUtils.isNull(memberVo, SmsExceptions.USER_ALREADY_EXIST);
                break;
            case RECOMMEND:    //推荐
                AssertUtils.hasLength(mobile, SmsExceptions.MOBILE_ERROR);
                break;
        }
        String smsCode = this.generateNewAuthCode(6);
        validateSMSParameters(mobile, smsType, smsCode);
        SmsRo smsRo = smsRedisDao.findSMSCode(mobile, smsType);
        if (smsRo != null) {
            boolean expression = TimeUnit.SECONDS.toMillis(sendDurationInSecond) > System.currentTimeMillis() - smsRo.getCreateTime();
            AssertUtils.isTrue(!expression, SmsExceptions.SMS_SEND_TIME_INVALID,
                    "%s秒内不能重复获取", sendDurationInSecond);
        }
        log.debug("Send {} sms code to {}, code={}", smsType, username, smsCode);
        Map<String, String> params = new HashMap<>();
        params.put("code", smsCode);
        sendMessage(smsType, params, mobile);
        smsRedisDao.saveSMSCodeAndAutoExpire(mobile, smsType, smsCode, validTimeInSecond);
    }


    /**
     * 校验验证码，验证通过，验证码失效
     */
    public Boolean validateAndDisableSMSCode(String mobile, SMSType smsType, String smsCode) {
        boolean validSMSCode = validateSMSCode(mobile, smsType, smsCode);
        if (validSMSCode) {
            log.debug("delete {} smsCode for mobile[{}]", smsType, smsCode, mobile);
            smsRedisDao.deleteSMSCode(mobile, smsType);
        }
        return validSMSCode;
    }

    /**
     * 校验验证码
     */
    public Boolean validateSMSCode(String mobile, SMSType smsType, String smsCode) {
        validateSMSParameters(mobile, smsType, smsCode);
        log.debug("validate {} smsCode[{}] for mobile[{}]", smsType, smsCode, mobile);
        SmsRo smsRo = smsRedisDao.findSMSCode(mobile, smsType);
        return (smsRo != null && smsCode.equalsIgnoreCase(smsRo.getCode()));
    }

    /**
     * 发送短信
     *
     * @param content 短信内容
     * @param mobile  手机号
     */
    private void sendMessage(String content, String mobile) {
        ShortMessageVo sm = new ShortMessageVo();
        sm.setContent(content);
        sm.setMobile(mobile);
        smsSendSource.getChannel().send(MessageBuilder.withPayload(sm).build());
    }

    /**
     * 发送短信
     *
     * @param mobile  手机号
     * @param params  替换参数
     * @param smsType 短信模板类型
     */
    private void sendMessage(SMSType smsType, Map<String, String> params, String mobile) {
        String shortMessageContent = smsTemplateService.intactMessage(smsType, params);
        sendMessage(shortMessageContent, mobile);
    }

    /**
     * 验证短信参数
     *
     * @param mobile  手机号
     * @param smsType 短信模板类型
     * @param smsCode 验证码
     */
    private void validateSMSParameters(String mobile, SMSType smsType, String smsCode) {
        AssertUtils.hasLength(mobile, SmsExceptions.MOBILE_ERROR);
        AssertUtils.notNull(smsType, SmsExceptions.MISSING_REQUIRED_PARAMS, "smsType不能为空");
        AssertUtils.hasLength(smsCode, SmsExceptions.MISSING_REQUIRED_PARAMS, "验证码不能为空");
    }

    /**
     * 生产验证码
     *
     * @param size 验证码位数
     */
    private String generateNewAuthCode(Integer size) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < size; i++) {
            sb.append(MathUtil.randomInt(0, 9));
        }
        return sb.toString();
    }
}
